﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace vueweb.helper
{
	public enum SortDirection
	{
		Asc,
		Desc
	}

	public class SortBuilder
	{
		private IQueryable _query;
		private IOrderedQueryable _queryAfterStart;
		private List<Tuple<string, SortDirection>> _sortData;

		public SortBuilder()
		{
			_sortData = new List<Tuple<string, SortDirection>>();
		}

		public void Add(string field, SortDirection dir = SortDirection.Asc)
		{
			_sortData.Add(new Tuple<string, SortDirection>(field, dir));
		}

		public IQueryable Build(IQueryable source)
		{
			if (_sortData == null || _sortData.Count == 0)
			{
				throw new Exception("sortData必须要有至少一条数据");
			}
			_query = source;
			var first = _sortData[0];
			Start(first.Item1, first.Item2);
			for (int i = 1; i < _sortData.Count; i++)
			{
				var next = _sortData[i];
				Then(next.Item1, next.Item2);
			}
			return _queryAfterStart;
		}

		public IQueryable<T> Build<T>(IQueryable source)
		{
			return Build(source) as IQueryable<T>;
		}

		private void Start(string field, SortDirection dir = SortDirection.Asc)
		{
			ParameterExpression parameter = Expression.Parameter(_query.ElementType, String.Empty);
			MemberExpression property = Expression.Property(parameter, field);
			LambdaExpression lambda = Expression.Lambda(property, parameter);
			string methodName = (dir == SortDirection.Asc) ? "OrderBy" : "OrderByDescending";

			MethodCallExpression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
												new Type[] { _query.ElementType, property.Type },
												_query.Expression, Expression.Quote(lambda));
			_queryAfterStart = _query.Provider.CreateQuery(methodCallExpression) as IOrderedQueryable;
		}

		private void Then(string field, SortDirection dir = SortDirection.Asc)
		{
			ParameterExpression parameter = Expression.Parameter(_query.ElementType, String.Empty);
			MemberExpression property = Expression.Property(parameter, field);
			LambdaExpression lambda = Expression.Lambda(property, parameter);
			string methodName = (dir == SortDirection.Asc) ? "ThenBy" : "ThenByDescending";

			MethodCallExpression methodCallExpression = Expression.Call(typeof(Queryable), methodName,
												new Type[] { _query.ElementType, property.Type },
												_queryAfterStart.Expression, Expression.Quote(lambda));
			_queryAfterStart = _queryAfterStart.Provider.CreateQuery(methodCallExpression) as IOrderedQueryable;
		}
	}
}
